﻿using Sidvall.IO;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Sidvall.Web.IO
{
    public class FileManager
    {
        #region Public Members

        #region CombineAsync

        public async Task<string> CombineAsync(string storeId, params string[] paths)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return fs.Combine(paths);
        }

        #endregion
        #region CopyFileAsync

        public async Task<bool> CopyFileAsync(string storeId, string sourceFileName, string destinationFileName, bool deleteExistingFile, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.GetItems, sourceFileName).ConfigureAwait(false);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.SaveItems, destinationFileName).ConfigureAwait(false);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.CopyFileAsync(sourceFileName, destinationFileName, deleteExistingFile, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region Delete

        public async Task<bool> DeleteFileAsync(string storeId, string path, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.DeleteItems, path).ConfigureAwait(false);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.DeleteFileAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region Exists

        public async Task<bool> FileExistsAsync(string storeId, string path, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.GetItemsMetadata, path).ConfigureAwait(false);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.FileExistsAsync(path, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region FlushAsync

        public async Task FlushAsync(string storeId, Sidvall.Web.IO.Data.OpenFileContext context, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await Sidvall.Web.IO.SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File,
                Sidvall.Security.Operations.SaveItems, context.Path).ConfigureAwait(false);
            var strategy = store.GetFileManagerStrategy();
            if (strategy != null)
            {
                await strategy.OnBeforeFlushAsync(store, context, cancellationToken).ConfigureAwait(false);
                if (context.Cancel)
                    return;
            }
            using (var result = await OpenFileAsync(store, context, Sidvall.IO.FileMode.Open, Sidvall.IO.FileAccess.Write, CancellationToken.None).ConfigureAwait(false))
            {
                await result.FlushAsync().ConfigureAwait(false);
            }
            if (strategy != null)
            {
                await strategy.OnAfterFlushAsync(store, context, cancellationToken).ConfigureAwait(false);
            }
        }

        #endregion
        #region GetItem

        public async Task<File> GetFileAsync(string storeId, string fileName, bool loadFileDetails, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.GetItemsMetadata, fileName).ConfigureAwait(false);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.GetFileAsync(fileName, loadFileDetails, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region GetItems

        public async Task<IEnumerable<File>> GetFilesAsync(string storeId, string path, string searchPattern, SearchOption searchOption, bool loadFileDetails, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.GetItemsMetadata, path).ConfigureAwait(false);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.GetFilesAsync(path, searchPattern, searchOption, loadFileDetails, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region GetNames

        public async Task<IEnumerable<string>> GetFileNamesAsync(string storeId, string path, string searchPattern, SearchOption searchOption, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.GetItemsMetadata, path).ConfigureAwait(false);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.GetFileNamesAsync(path, searchPattern, searchOption, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region Move

        public async Task<bool> MoveFileAsync(string storeId, string sourceFileName, string destinationFileName, bool deleteExistingFile, CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.SaveItems, sourceFileName).ConfigureAwait(false);
            await SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File, Sidvall.Security.Operations.SaveItems, destinationFileName).ConfigureAwait(false);
            var fs = await SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.MoveFileAsync(sourceFileName, destinationFileName, deleteExistingFile, cancellationToken).ConfigureAwait(false);
        }

        #endregion
        #region OpenFileAsync

        public async Task<System.IO.Stream> OpenFileAsync(string storeId, Sidvall.Web.IO.Data.OpenFileContext context, FileMode mode, FileAccess access,
            CancellationToken cancellationToken)
        {
            var store = Sidvall.Web.IO.SystemContext.Current.FileSystemContext.Stores.FindStore(storeId);
            switch (access)
            {
                case FileAccess.Read:
                    await Sidvall.Web.IO.SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File,
                         Sidvall.Security.Operations.GetItems, context.Path).ConfigureAwait(false);
                    break;
                default:
                    await Sidvall.Web.IO.SystemContext.Current.ThrowIfNotAuthorizedAsync(store, Sidvall.IO.Security.DomainObjects.File,
                        Sidvall.Security.Operations.SaveItems, context.Path).ConfigureAwait(false);
                    break;
            }
            var strategy = store.GetFileManagerStrategy();
            if (strategy != null)
            {
                await strategy.OnBeforeOpenFileAsync(store, context, mode, access, cancellationToken).ConfigureAwait(false);
                if (context.Cancel)
                    return null;
            }
            var stream = await OpenFileAsync(store, context, mode, access, cancellationToken).ConfigureAwait(false);
            if (strategy != null)
            {
                await strategy.OnAfterOpenFileAsync(store, context, mode, access, cancellationToken).ConfigureAwait(false);
            }
            return stream;
        }
        private async Task<System.IO.Stream> OpenFileAsync(Store store, Sidvall.Web.IO.Data.OpenFileContext context, FileMode mode, FileAccess access,
            CancellationToken cancellationToken)
        {
            var fs = await Sidvall.Web.IO.SystemContext.Current.FileSystemFactory.CreateAsync(store).ConfigureAwait(false);
            return await fs.OpenFileAsync(context.Path, mode, access, cancellationToken).ConfigureAwait(false);
        }

        #endregion

        #endregion
    }
}
